package com.yilin.platform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.reflect.TypeToken;
import com.yilin.platform.base_core.application.AppUtil;
import com.yilin.platform.base_core.application.PageData;
import com.yilin.platform.base_core.application.RedisClient;
import com.yilin.platform.base_core.application.BaseEntity;
import com.yilin.platform.base_core.commons.utils.*;
import com.yilin.platform.entity.wlhy.TmsRootData;
import com.yilin.platform.entity.user.deserialize.Account;
import com.yilin.platform.entity.user.deserialize.HatchTmsInterface;
import com.yilin.platform.mapper.wlhy.TmsRootDataMapper;
import com.yilin.platform.service.IPlatConfigService;
import com.yilin.platform.service.ITmsDataService;
import com.yilin.platform.service.IUserService;
import com.yilin.utils.SM2Utils;
import com.yilin.utils.SM4Utils;
import com.yilin.utils.Util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Service
@Transactional
public class TmsDataService extends ServiceImpl<TmsRootDataMapper, TmsRootData> implements ITmsDataService {

    private   Logger logger = LoggerFactory.getLogger(TmsDataService.class);
    private static String PLAT_TMS_DATA_LOGIN_KEY = "PLAT_TMS_DATA_LOGIN_KEY";

    @Resource
    TmsRootDataMapper tmsRootDataMapper;
    @Resource
    IPlatConfigService tmsConfigService;
    @Resource
    IUserService userService;

    private HatchTmsInterface hatchTmsInterface;

    private HatchTmsInterface getHatchTmsInterface(Account loginUser) {
        if (hatchTmsInterface == null) {
            hatchTmsInterface = userService.request("/user/hatch/getTmsInterfaceHatch", loginUser.getToken(),new HashMap<>(), new TypeToken<HatchTmsInterface>() {});
        }
        return hatchTmsInterface;
    }

    @Override
    public List<TmsRootData> syncUploadTmsDataList(Account loginUser) {
        //获取未上传数据，并同步保存到我的本地
        List<TmsRootData> tmsRootDataList = userService.request("/data/tms/copyTmsRootData", loginUser.getToken(), new HashMap<>(), new TypeToken<List<TmsRootData>>() {});
        for (int i = 0; i < tmsRootDataList.size(); i++) {
            TmsRootData tmsRootData = tmsRootDataList.get(i);
            TmsRootData identification = tmsRootDataMapper.selectOne(new QueryWrapper<TmsRootData>().eq("identification", tmsRootData.getIdentification()));
            logger.info("------------------------正在移植省运管数据------------------------");
            logger.info("正在转移数据...."+(i+1)+"/"+tmsRootDataList.size()+": "+tmsRootData.getIdentification());
            logger.info("------------------------正在移植省运管数据------------------------");
            if (identification != null) {
                identification.setJsonContent(tmsRootData.getJsonContent());
                tmsRootData.setPlatId(getHatchTmsInterface(loginUser).getPlatId());
                tmsRootData.setPlatName(getHatchTmsInterface(loginUser).getPlatName());
                tmsRootData.setDocumentVersionNumber("1.0");
                tmsRootDataMapper.updateById(identification);
            } else {
                tmsRootData.setStatus(TmsRootData.Status.transfer);
                tmsRootData.setPlatId(getHatchTmsInterface(loginUser).getPlatId());
                tmsRootData.setPlatName(getHatchTmsInterface(loginUser).getPlatName());
                tmsRootData.setDocumentVersionNumber("1.0");
                tmsRootDataMapper.insert(tmsRootData);
            }
        }
        if (tmsRootDataList.isEmpty()) return new ArrayList<>();
        //同步数据状态
        List<String> ids = ArrayUtil.fieldAdd(tmsRootDataList, BaseEntity::getId);
        userService.request("/data/tms/syncTmsRootData",loginUser.getToken(), MapUtil.generate(map -> {
            map.put("ids", String.join(",", ids));
            map.put("status", TmsRootData.Status.transfer);
        }), new TypeToken<Object>() {
        });
        return tmsRootDataList;
    }

    //获取本地待上传记录
    @Override
    public PageData<TmsRootData> getWaitUploadTmsDataPage(String ipcType, TmsRootData.Status status, String identification, Integer page, Integer limit) {
        if (status==null) status = TmsRootData.Status.transfer;
        QueryWrapper<TmsRootData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status",status.index);
        if (ipcType != null) queryWrapper.eq("ipc_type",ipcType);
        if (StringUtil.isNotEmpty(identification)) queryWrapper.like("identification",identification);
        return tmsRootDataMapper.selectPage(new PageData<>(page,limit),queryWrapper);
    }

    //获取本地待上传记录
    @Override
    public List<TmsRootData> getWaitUploadTmsDataPage(String ipcType, TmsRootData.Status status, String identification) {
        QueryWrapper<TmsRootData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status",status.index);
        if (ipcType != null) queryWrapper.eq("ipc_type",ipcType);
        if (StringUtil.isNotEmpty(identification)) queryWrapper.like("identification",identification);
        return tmsRootDataMapper.selectList(queryWrapper);
    }

    //自动从服务器获取记录 并提交
    //远程自动化
    @Override
    public void autoSyncWaitUploadTmsDataList(Account loginUser) {
        List<TmsRootData> waitUploadTmsDataList = syncUploadTmsDataList(loginUser);
        //提交到省运管
        for (TmsRootData tmsRootData : waitUploadTmsDataList) tmsPost(loginUser,tmsRootData);
        tmsConfigService.updateLastCount(waitUploadTmsDataList.size());
    }

    //本地.手动提交记录
    @Override
    public void postTmsDataByIds(Account loginUser, String[] ids) {
        List<TmsRootData> tmsRootDataList = tmsRootDataMapper.selectBatchIds(ArrayUtil.initList(ids));
        List<TmsRootData> waitUploadTmsDataList = ArrayUtil.whereBy(tmsRootDataList, obj -> obj.getStatus() != TmsRootData.Status.createSuccess);
        //提交到省运管
        for (TmsRootData tmsRootData : waitUploadTmsDataList) tmsPost(loginUser,tmsRootData);
        tmsConfigService.updateLastCount(waitUploadTmsDataList.size());
    }

    //本地.一键上送所有未提交数据
    @Override
    public void postAllTmsDataList(Account loginUser) {
        QueryWrapper<TmsRootData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status",TmsRootData.Status.transfer.index);
        List<TmsRootData> waitUploadTmsDataList = tmsRootDataMapper.selectList(queryWrapper);
        //提交到省运管
        for (TmsRootData tmsRootData : waitUploadTmsDataList) tmsPost(loginUser,tmsRootData);
        tmsConfigService.updateLastCount(waitUploadTmsDataList.size());
    }

    //本地.上送所有失败数据
    @Override
    public void postFailTmsDataList(Account loginUser) {
        QueryWrapper<TmsRootData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status",TmsRootData.Status.sendFail.index);
        List<TmsRootData> waitUploadTmsDataList = tmsRootDataMapper.selectList(queryWrapper);
        //提交到省运管
        for (TmsRootData tmsRootData : waitUploadTmsDataList) tmsPost(loginUser,tmsRootData);
        tmsConfigService.updateLastCount(waitUploadTmsDataList.size());
    }

    //贵州省网络货运请求服务
    private void tmsPost(Account loginUser,TmsRootData data) {
        String jsonContent = data.getJsonContent();
        logger.info(">>>>>>>>获取到传入的数据：文档名称：" + data.getDocumentName());
        logger.info(">>>>>>>>获取到传入的数据：业务接口代码：" + data.getIpcType());
        logger.info(">>>>>>>>获取到传入的数据：未加密报文单据：" + data.getJsonContent());
        HatchTmsInterface hatch = getHatchTmsInterface(loginUser);
        try {
            //获取登录Token和加密数据的Key
            Map<String, Object> tokenAndKey = getTokenAndKey(hatch);
            if (!(Boolean) tokenAndKey.get("success")) {
                data.setStatus(TmsRootData.Status.sendFail);
                data.setReturnState("FAIL");
                data.setReturnMsg("TMS登陆失败：" + tokenAndKey.get("msg"));
                tmsRootDataMapper.updateById(data);
                tmsConfigService.updatePostCount();
                return;
            }
            String publicKey = (String) tokenAndKey.get("key");
            String token = (String) tokenAndKey.get("token");
            //生成随机的对称加密秘钥
            String aesKey = AppUtil.getUUID().substring(0, 16);
            logger.info(">>>>>>>>生成随机的对称加密秘钥：" + aesKey);
            //加密报文表内容
            SM4Utils sm4 = new SM4Utils(aesKey, false);
            String encryptedContent = sm4.encryptData_ECB(data.getJsonContent());
            // 加密秘钥
            String encryptedCode = SM2Utils.encrypt(Util.hexToByte(publicKey), aesKey.getBytes());
            //组合报文最外层参数
            data.setUserName(hatch.getTmsUsername());
            data.setDocumentVersionNumber("1.0");
            data.setSenderCode(hatch.getSenderCode());
            data.setRecipientCode("wlhymot");
            data.setMessageSendingDateTime(DateTimeUtil.dateToString(new Date(), "yyyyMMddHHmmss"));
            data.setToken(token);
            data.setEncryptedCode(encryptedCode);
            data.setEncryptedContent(encryptedContent);
            data.setMessageReferenceNumber(AppUtil.getUUID().substring(0, 32));
            data.setIpcType(data.getIpcType());
            data.setJsonContent(null);
            String messageJson = JsonUtil.objectToJson(data);
            logger.info(">>>>>>>>参数构造成功，JSON为：" + messageJson);
            logger.info(">>>>>>>>正在发送请求....");
            String returnJson = HttpService.httpPostWithJson(hatch.getTmsDataUrl(), messageJson, null);
            data.setJsonContent(jsonContent);
            logger.info(">>>>>>>>请求返回成功：返回数据为：" + returnJson);
            Map<String, Object> returnMap = JsonUtil.jsonToObject(returnJson, new TypeToken<Map<String, Object>>() {
            });
            //自动提交次数+1https://guizhou.wlhy.org.cn/wlhy-exchange-kafka/message/sendMsg
            data.setAutoPostCount(data.getAutoPostCount() + 1);
            if (returnMap.get("code").equals("2001")) {
                logger.info(">>>>>>>>您本次请求失败：原因：Token失效，将重新登录并重新请求");
                RedisClient.getInstance().hdel(PLAT_TMS_DATA_LOGIN_KEY, "key");
                RedisClient.getInstance().hdel(PLAT_TMS_DATA_LOGIN_KEY, "token");
                //重新请求
                tmsPost(loginUser,data);
            } else if (returnMap.get("code").equals("1001")) {
                logger.info(">>>>>>>>请求成功：前往省级网络货运管理平台查看结果");
                //无论请求成功与否，保存数据到本地
                data.setStatus(TmsRootData.Status.sendSuccess);
                data.setReturnState("SUCCESS");
                data.setReturnMsg("TMS提交成功，前往省级网络货运管理平台查看结果");
                tmsRootDataMapper.updateById(data);
                tmsConfigService.updatePostCount();
            } else {
                data.setStatus(TmsRootData.Status.sendFail);
                data.setReturnState("FAIL");
                data.setReturnMsg("TMS提交失败：" + returnMap.get("message"));
                tmsRootDataMapper.updateById(data);
                tmsConfigService.updatePostCount();
            }
        } catch (Exception e) {
            logger.info("!!!!!!!!!!TMS数据请求异常：" + e.getMessage());
        }
    }

    private Map<String, Object> getTokenAndKey(HatchTmsInterface hatch) {
        Map<String, Object> tokenAndKey = new HashMap<>();
        //从Redis数据库去取token和keys
        String key = RedisClient.getInstance().hget(PLAT_TMS_DATA_LOGIN_KEY, "key");
        String token = RedisClient.getInstance().hget(PLAT_TMS_DATA_LOGIN_KEY, "token");
        if (key == null || token == null) {
            try {
                //获取登录需要的公钥
                String ms2KeyJson = HttpService.httpPostWithJson(hatch.getMs2KeyUrl(), "{}", null);
                //解析
                Map<String, Object> ms2KeyMap = JsonUtil.jsonToObject(ms2KeyJson, new TypeToken<Map<String, Object>>() {});
                Object ms2KeyObj = ((Map) ms2KeyMap.get("data")).get("publicKey");
                String ms2Key = ms2KeyObj.toString();
                logger.info(">>>>>>>>成功获取到登录需要的公钥：" + ms2Key);
                //登录参数构造
                Map<String, String> loginParam = new HashMap<>();
                //加密用户密码 SM2公钥加密密码 Util.hexToByte(publicKey)
                String encryptPassword = SM2Utils.encrypt(Util.hexToByte(ms2Key), hatch.getTmsPassword().getBytes());
                logger.info(">>>>>>>>加密后的密码：" + encryptPassword);
                loginParam.put("account", hatch.getTmsUsername());
                loginParam.put("passwd", encryptPassword);
                loginParam.put("publicRsa", ms2Key);
                loginParam.put("systemCode", null);
                //登录
                String loginJson = HttpService.httpPostWithJson(hatch.getTmsLoginUrl(), JsonUtil.objectToJson(loginParam), null);
                //解析
                Map<String, Object> loginMap = JsonUtil.jsonToObject(loginJson, new TypeToken<Map<String, Object>>() {});
                if (loginMap.get("data") == null) {
                    tokenAndKey.put("success", false);
                    tokenAndKey.put("msg", loginMap.get("message"));
                    return tokenAndKey;
                }
                token = (String) ((Map) loginMap.get("data")).get("token");
                key = (String) ((Map) loginMap.get("data")).get("PUBLIC_KEY");
                logger.info(">>>>>>>>登陆成功：");
                logger.info(">>>>>>>>获得Token数据：" + token);
                logger.info(">>>>>>>>获得PUBLIC_KEY数据：" + key);
                //保存Redis
                RedisClient.getInstance().hset(PLAT_TMS_DATA_LOGIN_KEY, "key", key);
                RedisClient.getInstance().hset(PLAT_TMS_DATA_LOGIN_KEY, "token", token);
                logger.info(">>>>>>>>Token和PUBLIC_KEY已保存到Redis数据库");
            } catch (Exception e) {
                logger.info("!!!!!!!!!TMS登录异常：" + e.getMessage());
            }
        }
        tokenAndKey.put("success", true);
        tokenAndKey.put("key", key);
        tokenAndKey.put("token", token);
        return tokenAndKey;
    }


}
